package org.utils.xml;

/**
 * @author Manuel EVENO
 * @version 1.3
 */

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

/**
 * This class provide parsing of XML data file, such as setup files, and produce output to a tree.
 */
public class XMLDataNodeParser extends DefaultHandler {
	private Locator locator = null;

	private XMLDataNode parsingTreeResult = null;

	private XMLDataNode currentNode = null;

	private boolean mElementLineInsertion = false;

	// Constructor
	public XMLDataNodeParser() {
		super();
	}

	private XMLDataNode parseInputSource(InputSource is,
			boolean pElementLineInsertion) throws SAXException, IOException,
			ParserConfigurationException {
		mElementLineInsertion = pElementLineInsertion;

		SAXParserFactory factory = SAXParserFactory.newInstance();
		factory.setValidating(false);
		factory.setNamespaceAware(false);
		SAXParser parser = factory.newSAXParser();
		parser.parse(is, this); // the parsing process will build the resulting tree ...

		// removing virtual root
		XMLDataNode root = (XMLDataNode) parsingTreeResult.getChildren().get(0);
		parsingTreeResult = null;
		root.setFather(null);
		return root;
	}

	public XMLDataNode parse(InputStream is, boolean pElementLineInsertion)
			throws SAXException, IOException, ParserConfigurationException {
		return parseInputSource(new InputSource(is), pElementLineInsertion);
	}

	public XMLDataNode parse(InputStream is) throws SAXException, IOException,
			ParserConfigurationException {
		return parseInputSource(new InputSource(is), false);
	}

	public XMLDataNode parse(File f, boolean pElementLineInsertion)
			throws SAXException, IOException, ParserConfigurationException {
		return parseInputSource(new InputSource(new FileInputStream(f)),
				pElementLineInsertion);
	}

	public XMLDataNode parse(File f) throws SAXException, IOException,
			ParserConfigurationException {
		return parseInputSource(new InputSource(new FileInputStream(f)), false);
	}

	////////////////////////////////////////////////////////////////////
	// Event handlers.
	////////////////////////////////////////////////////////////////////

	public final static String LINENUMBER_ATT = "_noLigne_";

	public void setDocumentLocator(Locator pLocator) {
		locator = pLocator;
	}

	public void startDocument() {
		// creating the root of the tree
		currentNode = parsingTreeResult = new XMLDataNode(
				"<root of the XML Data tree>", null);
	}

	public void startElement(String uri, String name, String qName,
			Attributes attributes) {
		String tagName = qName;
		if (qName == null || qName.length() == 0)
			tagName = name;
		XMLDataNode newNode = new XMLDataNode(tagName, currentNode);
		currentNode.addChild(newNode);
		currentNode = newNode;

		if (mElementLineInsertion)
			currentNode.addAttribute(LINENUMBER_ATT, Integer.toString(locator
					.getLineNumber()));

		// process parsed attibutes ...
		for (int i = 0; i < attributes.getLength(); i++)
			if (attributes.getQName(i) == null
					|| attributes.getQName(i).length() == 0)
				currentNode.addAttribute(attributes.getLocalName(i), attributes
						.getValue(i));
			else
				currentNode.addAttribute(attributes.getQName(i), attributes
						.getValue(i));
	}

	public void characters(char ch[], int start, int length) {
		String charsStr = new String(ch, start, length)
				/*.replace('\n', ' ')*/.trim();
		if (charsStr.length() != 0) // adding it only if not empty or space char filled
			currentNode.setValue(charsStr);
	}

	public void endElement(String uri, String name, String qName) {
		currentNode = currentNode.getFather();
	}
}
